package com.devilvan.betrice.bilibili.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.devilvan.betrice.bilibili.constant.BilibiliConstant;
import com.devilvan.betrice.bilibili.dto.BilibiliMemberDto;
import com.devilvan.betrice.bilibili.dto.BilibiliReviewDto;
import com.devilvan.betrice.bilibili.po.BilibiliReviewPo;
import com.devilvan.betrice.bilibili.service.BilibiliMemberService;
import com.devilvan.betrice.bilibili.service.BilibiliReviewService;
import com.devilvan.betrice.bilibili.vo.*;
import com.devilvan.betrice.common.BetriceResultCode;
import com.devilvan.betrice.common.BetriceResultVo;
import com.devilvan.betrice.request.BetriceHttpBuilder;
import com.devilvan.betrice.request.component.HttpClientComponent;
import com.devilvan.betrice.request.pojo.BetriceHttpClient;
import com.devilvan.betrice.util.UCollection;
import com.devilvan.betrice.util.UCopy;
import com.devilvan.betrice.util.UEmpty;
import com.devilvan.betrice.util.UString;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author Evad.Wu
 * @Description Bilibili-评论/回复信息表 控制器
 * @date 2022-12-22
 */
@RestController
@RequestMapping(value = "bilibiliReviewController")
@Tag(name = "Bilibili-评论/回复信息表 控制器")
public class BilibiliReviewController {
    @Resource(name = "httpClientComponent")
    private HttpClientComponent httpClientComponent;

    @Resource(name = "bilibiliReviewServiceImpl")
    private BilibiliReviewService bilibiliReviewService;
    @Resource(name = "bilibiliMemberServiceImpl")
    private BilibiliMemberService bilibiliMemberService;

    /**
     * 总请求控制器
     *
     * @param request 请求参数报文 {@link BilibiliReviewCrawlPageVo}
     * @param headers 请求头
     * @return 形式返回
     */
    @Operation(summary = "总请求控制器")
    @GetMapping(value = "doBilibiliReview")
    public BetriceResultVo<BilibiliReviewPo> doBilibiliReview(
            @RequestBody String request,
            @RequestHeader Map<String, String> headers) {
        BetriceResultVo<BilibiliReviewPo> betriceResultVo = new BetriceResultVo<>();
        BilibiliReviewCrawlPageVo bilibiliReviewCrawlPageVo = JSONObject.parseObject(request, BilibiliReviewCrawlPageVo.class);
        String csrf = bilibiliReviewCrawlPageVo.getCsrf();
        Map<String, String> subMap = UCollection.subMap(headers, "cookie");
        String aid = bilibiliReviewCrawlPageVo.getAid();
        Integer page = bilibiliReviewCrawlPageVo.getPage();
        Integer[] range = bilibiliReviewCrawlPageVo.getRange();
        if (Optional.ofNullable(range).isPresent()) {
            Integer start = range[0];
            Integer end = range[1];
            Map<String, Object> reviewCrawlerParams = new HashMap<>(16);
            reviewCrawlerParams.put("mode", 3);
            reviewCrawlerParams.put("plat", 1);
            reviewCrawlerParams.put("type", 1);
            if (UEmpty.isNotEmpty(csrf)) {
                reviewCrawlerParams.put("csrf", csrf);
            }
            for (int i = start; i <= end; i++) {
                reviewCrawlerParams.put("oid", aid);
                reviewCrawlerParams.put("next", i);
                // 评论采集
                String mainurl = UString.generateHttpParamUrl(BilibiliConstant.MAIN_URL_PREFIX, reviewCrawlerParams);
                BetriceHttpClient mainHttpClient = BetriceHttpBuilder.create().setHttpRequest(RequestMethod.GET, mainurl).setHeaders(subMap).build();
                String reviewCrawler = httpClientComponent.doExecute(mainHttpClient);
                List<BilibiliReviewVo> bilibiliReviewVoList = JSONObject.parseObject(reviewCrawler)
                        .getJSONObject("data").getJSONArray("replies").toJavaList(BilibiliReviewVo.class);
                List<BilibiliReviewDto> bilibiliReviewDtoList = this.bilibiliReviewVo2DtoList(bilibiliReviewVoList);
                List<BilibiliMemberDto> bilibiliMemberDtoList = this.bilibiliMemberVo2DtoList(bilibiliReviewVoList);
                List<Long> rpidList = bilibiliReviewVoList.stream().map(BilibiliReviewVo::getRpid).toList();
                Map<String, Object> replyParams = new HashMap<>(16);
                replyParams.put("oid", aid);
                replyParams.put("ps", 10);
                replyParams.put("type", 1);
                for (Long rpid : rpidList) {
                    replyParams.put("pn", 1);
                    replyParams.put("root", rpid);
                    int totalPage = -1;
                    int count = 1;
                    do {
                        String replyUrl = UString.generateHttpParamUrl(BilibiliConstant.REPLY_URL_PREFIX, replyParams);
                        BetriceHttpClient replyHttpClient = BetriceHttpBuilder.create().setHttpRequest(RequestMethod.GET, replyUrl).setHeaders(subMap).build();
                        String json = httpClientComponent.doExecute(replyHttpClient);
                        try {
                            JSONObject jsonObject = JSONObject.parseObject(json);
                            if (Optional.ofNullable(jsonObject).isEmpty()) {
                                replyParams.put("pn", count + 1);
                                continue;
                            }
                            JSONObject data = jsonObject.getJSONObject("data");
                            if (Optional.ofNullable(data).isEmpty()) {
                                replyParams.put("pn", count + 1);
                                continue;
                            }
                            JSONArray replies = data.getJSONArray("replies");
                            if (Optional.ofNullable(replies).isEmpty()) {
                                replyParams.put("pn", count + 1);
                                continue;
                            }
                            List<BilibiliReviewVo> replyVoList = replies.toJavaList(BilibiliReviewVo.class);
                            bilibiliReviewDtoList.addAll(this.bilibiliReviewVo2DtoList(replyVoList));
                            bilibiliMemberDtoList.addAll(this.bilibiliMemberVo2DtoList(replyVoList));
                            if (totalPage == -1) {
                                BilibiliReviewPageVo reviewPage = data.getObject("page", BilibiliReviewPageVo.class);
                                Integer total = reviewPage.getTotal();
                                totalPage = (int) Math.ceil(total.doubleValue() / reviewPage.getSize());
                            }
                            replyParams.put("pn", count + 1);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } while (count < totalPage);
                    replyParams.remove("pn");
                }
                bilibiliReviewService.distinctSaveReviewByRpid(bilibiliReviewDtoList);
                List<BilibiliMemberDto> distinctMemberDtoList = bilibiliMemberDtoList.stream()
                        .filter(UCopy.distinctByKey(BilibiliMemberDto::getMid)).toList();
                bilibiliMemberService.distinctSaveMemberByMid(distinctMemberDtoList);
            }
        } else if (Optional.ofNullable(page).isPresent() && page >= 1) {
            Map<String, Object> reviewCrawlerParams = new HashMap<>(16);
            reviewCrawlerParams.put("mode", 3);
            reviewCrawlerParams.put("oid", aid);
            reviewCrawlerParams.put("next", page);
            reviewCrawlerParams.put("plat", 1);
            reviewCrawlerParams.put("type", 1);
            if (UEmpty.isNotEmpty(csrf)) {
                reviewCrawlerParams.put("csrf", csrf);
            }
            // 评论采集
            String mainurl = UString.generateHttpParamUrl(BilibiliConstant.MAIN_URL_PREFIX, reviewCrawlerParams);
            BetriceHttpClient mainHttpClient = BetriceHttpBuilder.create().setHttpRequest(RequestMethod.GET, mainurl).setHeaders(subMap).build();
            String reviewCrawler = httpClientComponent.doExecute(mainHttpClient);
            List<BilibiliReviewVo> bilibiliReviewVoList = JSONObject.parseObject(reviewCrawler)
                    .getJSONObject("data").getJSONArray("replies").toJavaList(BilibiliReviewVo.class);
            List<BilibiliReviewDto> bilibiliReviewDtoList = this.bilibiliReviewVo2DtoList(bilibiliReviewVoList);
            List<BilibiliMemberDto> bilibiliMemberDtoList = this.bilibiliMemberVo2DtoList(bilibiliReviewVoList);
            List<Long> rpidList = bilibiliReviewVoList.stream().map(BilibiliReviewVo::getRpid).toList();
            Map<String, Object> replyParams = new HashMap<>(16);
            replyParams.put("oid", aid);
            replyParams.put("ps", 10);
            replyParams.put("type", 1);
            for (Long rpid : rpidList) {
                replyParams.put("pn", 1);
                replyParams.put("root", rpid);
                int totalPage = -1;
                int count = 1;
                do {
                    String replyUrl = UString.generateHttpParamUrl(BilibiliConstant.REPLY_URL_PREFIX, replyParams);
                    BetriceHttpClient replyHttpClient = BetriceHttpBuilder.create().setHttpRequest(RequestMethod.GET, replyUrl).setHeaders(subMap).build();
                    String json = httpClientComponent.doExecute(replyHttpClient);
                    try {
                        JSONObject jsonObject = JSONObject.parseObject(json);
                        if (Optional.ofNullable(jsonObject).isEmpty()) {
                            replyParams.put("pn", count + 1);
                            continue;
                        }
                        JSONObject data = jsonObject.getJSONObject("data");
                        if (Optional.ofNullable(data).isEmpty()) {
                            replyParams.put("pn", count + 1);
                            continue;
                        }
                        JSONArray replies = data.getJSONArray("replies");
                        if (Optional.ofNullable(replies).isEmpty()) {
                            replyParams.put("pn", count + 1);
                            continue;
                        }
                        List<BilibiliReviewVo> replyVoList = replies.toJavaList(BilibiliReviewVo.class);
                        bilibiliReviewDtoList.addAll(this.bilibiliReviewVo2DtoList(replyVoList));
                        bilibiliMemberDtoList.addAll(this.bilibiliMemberVo2DtoList(replyVoList));
                        if (totalPage == -1) {
                            BilibiliReviewPageVo reviewPage = data.getObject("page", BilibiliReviewPageVo.class);
                            Integer total = reviewPage.getTotal();
                            totalPage = (int) Math.ceil(total.doubleValue() / reviewPage.getSize());
                        }
                        replyParams.put("pn", count + 1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } while (count < totalPage);
                replyParams.remove("pn");
            }
            bilibiliReviewService.distinctSaveReviewByRpid(bilibiliReviewDtoList);
            List<BilibiliMemberDto> distinctMemberDtoList = bilibiliMemberDtoList.stream()
                    .filter(UCopy.distinctByKey(BilibiliMemberDto::getMid)).toList();
            bilibiliMemberService.distinctSaveMemberByMid(distinctMemberDtoList);
        } else {
            betriceResultVo.result(BetriceResultCode.FAILED_500, "page和range参数不能都为空！");
            return betriceResultVo;
        }
        return betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
    }

    /**
     * 采集评论报文控制器
     *
     * @param aid     视频ID
     * @param page    页码
     * @param headers 请求头信息
     * @return 形式返回
     */
    @Operation(summary = "采集报文控制器")
    @GetMapping(value = "doBilibiliReviewCrawler")
    public BetriceResultVo<BilibiliReviewPo> doBilibiliReviewCrawler(
            @RequestParam String aid,
            @RequestParam Integer page,
            @RequestHeader Map<String, String> headers) {
        Map<String, String> subMap = UCollection.subMap(headers, "cookie");
        Map<String, Object> params = new HashMap<>(16);
        params.put("mode", 3);
        params.put("oid", aid);
        params.put("next", page);
        params.put("plat", 1);
        params.put("type", 1);
        String mainUrlPrefix = "https://api.bilibili.com/x/v2/reply/main";
        String url = UString.generateHttpParamUrl(mainUrlPrefix, params);
        BetriceHttpClient betriceHttpClient = BetriceHttpBuilder.create().setHttpRequest(RequestMethod.GET, url).setHeaders(subMap).build();
        String json = httpClientComponent.doExecute(betriceHttpClient);
        return this.doBilibiliReviewRequest(json);
    }

    /**
     * 采集评论下的评论/回复 报文控制器
     *
     * @param aid     视频ID
     * @param page    页码
     * @param root    所属评论的ID
     * @param headers 请求头信息
     * @return 形式返回
     */
    @Operation(summary = "采集评论下的评论/回复 报文控制器")
    @GetMapping(value = "doBilibiliReplyCrawler")
    public BetriceResultVo<BilibiliReviewPo> doBilibiliReplyCrawler(
            @RequestParam String aid,
            @RequestParam Long root,
            @RequestHeader Map<String, String> headers) {
        BetriceResultVo<BilibiliReviewPo> betriceResultVo = new BetriceResultVo<>();
        Map<String, String> subMap = UCollection.subMap(headers, "cookie");
        Map<String, Object> params = new HashMap<>(16);
        params.put("oid", aid);
        params.put("pn", 1);
        params.put("ps", 10);
        params.put("root", root);
        params.put("type", 1);
        int totalPage = -1;
        int count = 1;
        do {
            String replyUrlPrefix = "https://api.bilibili.com/x/v2/reply/reply";
            String url = UString.generateHttpParamUrl(replyUrlPrefix, params);
            BetriceHttpClient betriceHttpClient = BetriceHttpBuilder.create().setHttpRequest(RequestMethod.GET, url).setHeaders(subMap).build();
            String json = httpClientComponent.doExecute(betriceHttpClient);
            this.doBilibiliReviewRequest(json);
            if (totalPage == -1) {
                BilibiliReviewPageVo reviewPage = JSONObject.parseObject(json)
                        .getJSONObject("data").getObject("page", BilibiliReviewPageVo.class);
                Integer total = reviewPage.getTotal();
                totalPage = (int) Math.ceil(total.doubleValue() / reviewPage.getSize());
            }
            params.put("pn", count + 1 + 1);
        } while (count < totalPage);
        return betriceResultVo;
    }

    /**
     * 解析评论报文控制器
     *
     * @param request 报文
     * @return 形式返回
     */
    @Operation(summary = "解析评论报文控制器")
    @GetMapping(value = "doBilibiliReviewRequest")
    public BetriceResultVo<BilibiliReviewPo> doBilibiliReviewRequest(@RequestBody String request) {
        BetriceResultVo<BilibiliReviewPo> betriceResultVo = new BetriceResultVo<>();
        try {
            List<BilibiliReviewVo> bilibiliReviewVoList = JSONObject.parseObject(request)
                    .getJSONObject("data").getJSONArray("replies").toJavaList(BilibiliReviewVo.class);
            List<BilibiliReviewDto> bilibiliReviewDtoList = this.bilibiliReviewVo2DtoList(bilibiliReviewVoList);
            List<BilibiliMemberDto> bilibiliMemberDtoList = this.bilibiliMemberVo2DtoList(bilibiliReviewVoList);
            bilibiliReviewService.distinctSaveReviewByRpid(bilibiliReviewDtoList);
            bilibiliMemberService.distinctSaveMemberByMid(bilibiliMemberDtoList);
            betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
        } catch (Exception e) {
            e.printStackTrace();
            betriceResultVo.result(BetriceResultCode.FAILED_500, "操作失败！");
        }
        return betriceResultVo;
    }

    /**
     * 解析评论下的评论/回复 报文控制器
     *
     * @param request 报文
     * @return 形式返回
     */
    @Operation(summary = "解析评论下的评论/回复 报文控制器")
    @GetMapping(value = "doBilibiliReplyRequest")
    public BetriceResultVo<BilibiliReviewPo> doBilibiliReplyRequest(@RequestBody String request) {
        BetriceResultVo<BilibiliReviewPo> betriceResultVo = new BetriceResultVo<>();
        try {
            List<BilibiliReviewVo> bilibiliReviewVoList = JSONObject.parseObject(request)
                    .getJSONObject("data").getJSONArray("replies").toJavaList(BilibiliReviewVo.class);
            List<BilibiliReviewDto> bilibiliReviewDtoList = this.bilibiliReviewVo2DtoList(bilibiliReviewVoList);
            List<BilibiliMemberDto> bilibiliMemberDtoList = this.bilibiliMemberVo2DtoList(bilibiliReviewVoList);
            bilibiliReviewService.distinctSaveReviewByRpid(bilibiliReviewDtoList);
            bilibiliMemberService.distinctSaveMemberByMid(bilibiliMemberDtoList);
            betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
        } catch (Exception e) {
            e.printStackTrace();
            betriceResultVo.result(BetriceResultCode.FAILED_500, "操作失败！");
        }
        return betriceResultVo;
    }

    /**
     * {@link BilibiliReviewVo} -> {@link BilibiliMemberDto}
     *
     * @param bilibiliReviewVoList vo list
     * @return dto list
     */
    private List<BilibiliMemberDto> bilibiliMemberVo2DtoList(List<BilibiliReviewVo> bilibiliReviewVoList) {
        List<BilibiliMemberDto> bilibiliMemberDtoList = new ArrayList<>();
        for (BilibiliReviewVo bilibiliReviewVo : bilibiliReviewVoList) {
            BilibiliMemberDto bilibiliMemberDto = new BilibiliMemberDto();
            BilibiliMemberVo bilibiliMemberVo = bilibiliReviewVo.getBilibiliMemberVo();
            bilibiliMemberDto.setMid(bilibiliMemberVo.getMid());
            bilibiliMemberDto.setName(bilibiliMemberVo.getName());
            bilibiliMemberDto.setSex(bilibiliMemberVo.getSex());
            bilibiliMemberDto.setSign(bilibiliMemberVo.getSign());
            bilibiliMemberDto.setLevel(bilibiliMemberVo.getBilibiliMemberLevelVo().getLevel());
            bilibiliMemberDtoList.add(bilibiliMemberDto);
        }
        return bilibiliMemberDtoList;
    }

    /**
     * {@link BilibiliReviewVo} -> {@link BilibiliReviewDto}
     *
     * @param bilibiliReviewVoList vo list
     * @return dto list
     */
    private List<BilibiliReviewDto> bilibiliReviewVo2DtoList(List<BilibiliReviewVo> bilibiliReviewVoList) {
        List<BilibiliReviewDto> bilibiliReviewDtoList = new ArrayList<>();
        for (BilibiliReviewVo bilibiliReviewVo : bilibiliReviewVoList) {
            BilibiliReviewDto bilibiliReviewDto = new BilibiliReviewDto();
            bilibiliReviewDto.setAid(bilibiliReviewVo.getAid());
            bilibiliReviewDto.setRpid(bilibiliReviewVo.getRpid());
            bilibiliReviewDto.setCtime(new Date(bilibiliReviewVo.getCtime() * 1000L));
            // 评论用户
            BilibiliMemberVo bilibiliMemberVo = bilibiliReviewVo.getBilibiliMemberVo();
            bilibiliReviewDto.setMid(bilibiliMemberVo.getMid());
            bilibiliReviewDto.setName(bilibiliMemberVo.getName());
            // 回复用户
            BilibiliReviewContentVo bilibiliReviewContentVo = bilibiliReviewVo.getBilibiliReviewContentVo();
            Optional.ofNullable(bilibiliReviewContentVo).ifPresent(bilibiliReviewContent -> {
                bilibiliReviewDto.setContent(bilibiliReviewContent.getContent());
                List<BilibiliMemberVo> bilibiliMemberVoList = bilibiliReviewContent.getBilibiliMemberVoList();
                if (UEmpty.isNotEmpty(bilibiliMemberVoList)) {
                    bilibiliReviewDto.setRepliedId(bilibiliMemberVoList.get(0).getMid());
                    bilibiliReviewDto.setRepliedName(bilibiliMemberVoList.get(0).getName());
                }
            });
            bilibiliReviewDto.setLikes(bilibiliReviewVo.getLikes());
            bilibiliReviewDto.setRoot(bilibiliReviewVo.getRoot());
            bilibiliReviewDto.setParent(bilibiliReviewVo.getParent());
            Optional.ofNullable(bilibiliReviewVo.getBilibiliReviewLocationVo())
                    .map(BilibiliReviewLocationVo::getLocation)
                    .ifPresent(location -> bilibiliReviewDto.setLocation(location.replace("IP属地：", "")));
            bilibiliReviewDtoList.add(bilibiliReviewDto);
        }
        return bilibiliReviewDtoList;
    }
}
